package com.ushine.web.component.scheduled;

import cn.hutool.core.date.DateUtil;
import com.ushine.web.component.exception.ServiceException;
import com.ushine.web.component.exception.ServiceExceptionEnum;
import com.ushine.web.component.scheduled.config.QuartzJobQuartzConfig;
import com.ushine.web.component.scheduled.model.QuartzJobDO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

/**
 * @Author: yls
 * @Date 2022/2/18 13:49
 * @Description: 定时器工具类
 * @Version 1.0
 **/
@Slf4j
@Component
public class QuartzService {
    /**
     * 来自 【yml】中配置的【instanceName】项
     */
    @Resource
    private Scheduler scheduler;

    @Resource
    private QuartzJobQuartzConfig quartzJobQuartzConfig;

    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            log.info("exception:", e);
        }
    }

    /**
     * 功能描述:
     * 〈添加一个带次数的job〉
     *
     * @param jobClass     任务实现类
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes     运行的次数 （<0:表示不限次数）
     * @param jobData      参数
     * @return : void
     * @author : yls
     * @date : 2022/2/18 16:03
     */
    public void insertJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
                          int jobTimes, Map jobData) throws SchedulerException {
        // 任务名称和组构成任务key
        JobDetail jobDetail = quartzJobQuartzConfig.getJobDetail(jobClass, jobName, jobGroupName);
        // 设置job参数
        if (jobData != null && jobData.size() > 0) {
            jobDetail.getJobDataMap().putAll(jobData);
        }
        // 使用simpleTrigger规则
        Trigger trigger;
        if (jobTimes < 0) {
            trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                    .startNow().build();
        } else {
            trigger = TriggerBuilder
                    .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
                            .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                    .startNow().build();
        }
        scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 功能描述:
     * 〈 添加任务
     * 定义调度触发规则
     * 使用cornTrigger规则
     * 触发器key
     * Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
     * 创建时不自动启动，采用手动启动的方式
     * .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
     * .withSchedule(cronScheduleBuilder).withDescription(description).build();
     *
     * @param jobClass     具体的定时任务实现类
     * @param jobName      任务名称(建议唯一)
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 （如：0/5 * * * * ? ） 编辑的时候注意格式哟
     * @param description  描述信息
     * @param jobData      业务自定义参数
     * @return : void
     * @author : yls
     * @date : 2022/2/18 14:01
     */
    public void insertJob(Class<? extends QuartzJobBean> jobClass,
                          String jobName,
                          String jobGroupName,
                          String jobTime,
                          String description,
                          Map jobData) throws SchedulerException {

        /*
         * 创建jobDetail实例，绑定Job实现类
         * 指明job的名称，所在组的名称，以及绑定job类
         * 任务名称和组构成任务key
         */
        JobDetail jobDetail = quartzJobQuartzConfig.getJobDetail(jobClass, jobName, jobGroupName);
        // 设置job参数
        if (jobData != null && jobData.size() > 0) {
            jobDetail.getJobDataMap().putAll(jobData);
        }
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobTime);
        Trigger trigger = quartzJobQuartzConfig.getCronTrigger(jobDetail, cronScheduleBuilder, jobName, jobGroupName, description);

        // 把作业和触发器注册到任务调度中
        scheduler.scheduleJob(jobDetail, trigger);
    }


    /**
     * 功能描述:
     * 〈修改任务:暂时不支持修改参数
     * if(!trigger.getJobDataMap().get("invokeParam").equals(appQuartz.getInvokeParam())) {
     * trigger.getJobDataMap().put("invokeParam",appQuartz.getInvokeParam());
     * }
     * 〉
     *
     * @param appQuartz 任务定时器
     * @return : java.lang.String
     * @author : yls
     * @date : 2022/2/18 14:05
     */
    public Boolean updateJob(QuartzJobDO appQuartz) throws SchedulerException, ServiceException {
        if (!CronExpression.isValidExpression(appQuartz.getCronExpression())) {
            log.info("表达式不正确");
            return false;
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(appQuartz.getJobName(), appQuartz.getJobGroupName());
        JobKey jobKey = new JobKey(appQuartz.getJobName(), appQuartz.getJobGroupName());
        if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //表达式调度构建器,不立即执行
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(appQuartz.getCronExpression()).withMisfireHandlingInstructionDoNothing();
            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder).withDescription(appQuartz.getDescription()).build();
            scheduler.rescheduleJob(triggerKey, trigger);
            return true;
        } else {
            throw new ServiceException(ServiceExceptionEnum.QUARTZ_TASK_NOT_EXIST);
        }
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务所属组名称
     * @param jobTime      时间表达式
     */
    public Boolean updateJob(String jobName, String jobGroupName, String jobTime) throws SchedulerException, ServiceException {
        if (!CronExpression.isValidExpression(jobTime)) {
            throw new ServiceException(ServiceExceptionEnum.QUARTZ_CRON_ERROR);
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
        // 重启触发器
        scheduler.rescheduleJob(triggerKey, trigger);
        return true;
    }


    /**
     * 功能描述:
     * 〈 删除任务 〉
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @return : void
     * @author : yls
     * @date : 2022/2/18 14:04
     */
    public void deleteJob(String jobName, String jobGroupName) throws SchedulerException {
        scheduler.deleteJob(new JobKey(jobName, jobGroupName));
    }


    /**
     * 功能描述:
     * 〈获取所有计划中的任务列表〉
     *
     * @return : java.util.List<com.ushine.web.component.scheduled.model.QuartzJobDO>
     * @author : yls
     * @date : 2022/2/18 14:38
     */
    public List<QuartzJobDO> listAllJobs() throws SchedulerException {
        List<QuartzJobDO> jobList = new ArrayList<>();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                QuartzJobDO quartzJob = new QuartzJobDO();
                quartzJob.setJobName(jobKey.getName())
                        .setJobGroupName(jobKey.getGroup())
                        .setJobClass("")
                        .setDescription(trigger.getDescription());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                quartzJob.setJobStatus(triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    quartzJob.setCronExpression(cronExpression);
                    quartzJob.setNextTime(DateUtil.formatTime(cronTrigger.getNextFireTime()));
                }
                jobList.add(quartzJob);
            }
        }
        return jobList;
    }

    /**
     * 功能描述:
     * 〈获取所有正在运行的job〉
     *
     * @param
     * @return : java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     * @author : yls
     * @date : 2022/2/18 16:11
     */
    public List<Map<String, Object>> listRunJobs() throws SchedulerException {
        List<Map<String, Object>> jobList = null;
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        jobList = new ArrayList<>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            Map<String, Object> map = new HashMap<>(10);
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            QuartzJobDO quartzJob = new QuartzJobDO();
            quartzJob.setJobName(jobKey.getName())
                    .setJobGroupName(jobKey.getGroup())
                    .setJobClass("")
                    .setJobStatus(triggerState.name())
                    .setDescription(trigger.getDescription());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                quartzJob.setCronExpression(cronExpression);
                quartzJob.setNextTime(DateUtil.formatTime(cronTrigger.getNextFireTime()));
            }
            jobList.add(map);
        }
        return jobList;
    }

    /**
     * 功能描述:
     * 〈暂停任务〉
     *
     * @param jobName  任务名称
     * @param jobGroup 任务组
     * @return : java.lang.String
     * @author : yls
     * @date : 2022/2/18 14:03
     */
    public Boolean pauseJob(String jobName, String jobGroup) throws SchedulerException, ServiceException {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            throw new ServiceException(ServiceExceptionEnum.QUARTZ_TASK_NOT_EXIST);
        } else {
            scheduler.pauseJob(jobKey);
            return true;
        }

    }

    /**
     * 功能描述:
     * 〈立即执行一个job〉
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务所属组
     * @return : void
     * @author : yls
     * @date : 2022/2/18 16:07
     */
    public Boolean runOneJobNow(String jobName, String jobGroupName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        scheduler.triggerJob(jobKey);
        return true;
    }


    /**
     * 功能描述:
     * 〈 恢复某个任务 〉
     *
     * @param jobName  任务名称
     * @param jobGroup 任务所属组
     * @return : java.lang.String
     * @author : yls
     * @date : 2022/2/18 14:02
     */
    public Boolean resumeJob(String jobName, String jobGroup) throws SchedulerException, ServiceException {
        JobKey jobKey = new JobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            throw new ServiceException(ServiceExceptionEnum.QUARTZ_CRON_ERROR);
        } else {
            scheduler.resumeJob(jobKey);
            return true;
        }
    }
}
